package pro.images.server.datasource;

import com.alibaba.druid.pool.DruidDataSource;
import io.shardingjdbc.core.api.ShardingDataSourceFactory;
import io.shardingjdbc.core.api.config.ShardingRuleConfiguration;
import io.shardingjdbc.core.api.config.TableRuleConfiguration;
import io.shardingjdbc.core.api.config.strategy.InlineShardingStrategyConfiguration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * <p>
 * 初始化数据源
 * </p>
 *
 * @author 杨阳
 * @since 2017年10月16日
 */
@Configuration
@MapperScan(basePackages = {"pro.images.server.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory")
public class DataSourceConfig {
    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;
    @Value("${spring.datasource.url}")
    private String url;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;
    //分表的数量
    private int tables = 5;

    /**
     * <p>
     * 基础数据库
     * </p>
     *
     * @return {@link DataSource}
     */
    @Bean(name = "dataSource")
    public DataSource dataSource() throws SQLException {
        //因为这里要用分库，所以配置的是Sharding-jdbc的数据源
        // 配置真实数据源
        Map<String, DataSource> dataSourceMap = new HashMap<String, DataSource>(1);

        DruidDataSource dataSource1 = new DruidDataSource();
        dataSource1.setDriverClassName(driverClassName);
        dataSource1.setUrl(url);
        dataSource1.setUsername(username);
        dataSource1.setPassword(password);

        dataSourceMap.put("dataSource", dataSource1);

        //配置t_images分表规则
        TableRuleConfiguration imagesTableRuleConfig = new TableRuleConfiguration();
        //这里配置的是表的基础名称，分表后可能会是t_images_0，t_images_1
        imagesTableRuleConfig.setLogicTable("t_images");

        /*
         *  dataSource_${0..1}.t_order_${0..1}规则解释：
         *  数据源名称.表名，因为上面的例子中即分库又分表，所以 写法是 数据源名称_分库策略.表名_分表策略
         */
        imagesTableRuleConfig.setActualDataNodes("dataSource.t_images_${0.." + tables + "}");

        /*
         *  配置分库策略,
         *  ds_${user_id % 2} 表示 利用user_id字符取余后分配到库中，这里不想分库，试一下直接把后面的地方写死为dataSource
         */
        imagesTableRuleConfig.setDatabaseShardingStrategyConfig(
                new InlineShardingStrategyConfiguration("id", "dataSource"));

        /*
         * 配置分表策略
         */
        imagesTableRuleConfig.setTableShardingStrategyConfig(
                new InlineShardingStrategyConfiguration("id", "t_images_${id % " + tables + "}"));

        // 配置分片规则
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTableRuleConfigs().add(imagesTableRuleConfig);

        Properties props = new Properties();
        props.setProperty("sql.show", "true");
        //表示执行语句时使用多线程个数，这里最好是根据cpu的核算进行计算
        props.setProperty("executor.size", "5");

        // 获取数据源对象
        return ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, props);
    }

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionTemplate(@Qualifier("dataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:mapper/*.xml"));
        bean.setConfigLocation(new ClassPathResource("mybatis/mybatis-config.xml"));
        return bean.getObject();
    }

    @Bean(name = "transactionManager")
    public DataSourceTransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }


}

